<!DOCTYPE html> <!-- -*- tab-width: 4; indent-tabs-mode: nil; fill-column: 100 -*- -->

<!--
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
-->

<!-- Note that this is *not* intended to match what the real loleaflet.html looks like, especially
    with regards to the iframes. That this document nests another copy of itself inside an iframe is
    just a fun hack. The real loleaflet.html, when used in an iframe (in the Nextcloud app), is
    nested in a totally different page.

-->

<html>
  <head>
    <title>
      COKbdMgrTest Test Page
    </title>

    <script>
      const getParameterByName = function (name) {
          name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
          var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
          var results = regex.exec(location.search);
          return results === null ? '' : results[1].replace(/\+/g, ' ');
      };

      const depth = getParameterByName("depth") ? Number(getParameterByName("depth")) : 0;

      // The embedding iOS app calls this function when the user is inserting text with the
      // keyboard. It is called regardless whether it is in the outermost document or inside in an
      // iframe, thanks to the event listener in the WKUserScript that
      // CollaboraOnlineWebViewKeyboardManager injects into all iframes of the WKWebView.

      // The app here, COKbdMgrTest, is just a test-bed for CollaboraOnlineWebViewKeyboardManager.
      // The real (and only) other use-case for it is the JS over in Collabora Online's loleaflet
      // directory. That will use a similar window.COKbdMgrCallback function. (Depending on in what
      // context the keyboard is needed, slightly different functions might be assigned to
      // window.COKbdMgrCallback.)

      // Possibly the sanity check part of this should be factored out into a separate function that
      // would be installed in the WKUserScript that CollaboraOnlineWebViewKeyboardManager injects.

      window.COKbdMgrCallback = function(message) {
          if (typeof message !== 'object') {
              const errorMessage = 'COKbdMgrCallback called with non-object of type ' + typeof message;
              console.log(errorMessage);
              throw errorMessage;
          }

          if (message.id !== 'COKbdMgr') {
              const errorMessage = 'COKbdMgrCallback called with object with unknown id: ' + message.id;
              console.log(errorMessage);
              throw errorMessage;
          }

          if (message.command === undefined || typeof message.command !== 'string') {
              const errorMessage = 'COKbdMgrCallback called without command';
              console.log(errorMessage);
              throw errorMessage;
          }

          if (message.command === 'replaceText') {
              if (message.text === undefined || typeof message.text !== 'string') {
                  const errorMessage = 'COKbdMgrCallback called for replaceText without text';
                  console.log(errorMessage);
                  throw errorMessage;
              }

              if (message.location === undefined || typeof message.location !== 'number') {
                  const errorMessage = 'COKbdMgrCallback called for replaceText without location';
                  console.log(errorMessage);
                  throw errorMessage;
              }

              if (message.length === undefined || typeof message.length !== 'number') {
                  const errorMessage = 'COKbdMgrCallback called for replaceText without length';
                  console.log(errorMessage);
                  throw errorMessage;
              }

              // When a "q" is typed we ask the embedding iOS app to hide the keyboard.
              if (message.text === 'q') {
                  // Posting message to the embedding iOS app does not require any tricks through
                  // surrounding iframes but can be done directly.
                  window.webkit.messageHandlers.CollaboraOnlineWebViewKeyboardManager.postMessage({command: 'hide'});
                  return;
              }

              var t = document.getElementById("editable-text");

              if (message.location > t.innerHTML.length)
                  message.location = t.innerHTML.length;

              var pre = t.innerHTML.substring(0, message.location);
              var post = t.innerHTML.substring(message.location + message.length);
              t.innerHTML = pre + message.text + post;
          } else if (message.command === 'deleteBackward') {
              var t = document.getElementById("editable-text");
              if (t.innerHTML !== '') {
                  t.innerHTML = t.innerHTML.substring(0, t.innerHTML.length - 1);
              }
          } else {
              const errorMessage = 'COKbdMgrCallback called with unknown command ' + message.command;
                  console.log(errorMessage);
                  throw errorMessage;
          }
      }

      const displayKeyboard = function(type) {
          // Sure, here in this demo app we know that we are embedded in an iOS app that uses
          // CollaboraOnlineWebViewKeyboardManager, so the below will always exist, but as an
          // example, check anyway.
          if (window.webkit &&
              window.webkit.messageHandlers &&
              window.webkit.messageHandlers.CollaboraOnlineWebViewKeyboardManager) {
              var t = document.getElementById("editable-text");

              // The 'display' command takes additional properties 'type' for the keyboard type,
              // 'text' for the text to "seed" the control with, what we want it to pretend we are
              // editing (if we know it), and 'location' for the editing location in that text, if
              // we know it (UINT_MAX otherwise).

              window.webkit.messageHandlers.CollaboraOnlineWebViewKeyboardManager.postMessage({command: 'display',
                                                                                               type: type,
                                                                                               text: t.innerHTML,
                                                                                               location: t.innerHTML.length}); }
      }

      const addIframe = function() {
          // Only add it once
          var i = document.getElementById("iframe");
          if (i)
              return;

          // Hide keyboard if we are displaying it
          window.webkit.messageHandlers.CollaboraOnlineWebViewKeyboardManager.postMessage({command: 'hide'});

          // Remove the interaction part from the parent page as it
          // would be too confusing to keep it, too.
          var t = document.getElementById("interaction");
          t.remove();

          // Undefine the window.COKbdMgrCallback function so that the JS that
          // CollaboraOnlineWebViewKeyboardManager executes will post a message to the iframe
          // instead.
          window.COKbdMgrCallback = undefined;

          // Instead add an explanatory paragraph.
          var p = document.createElement("p");
          p.style.fontSize = "40px";
          p.innerHTML = "Here is an iframe (depth " + (depth + 1) + "):";
          document.body.appendChild(p);

          // Create the iframe, using this same html file.
          i = document.createElement("iframe");
          i.id = "iframe";
          i.src = document.location.href.replace(/\?depth=\d+$/, "") + "?depth=" + (depth + 1);
          i.width = innerWidth - 100;
          i.height = innerHeight - 100;
          i.allow = "clipboard-read *; clipboard-write *";
          const c = 255 - (depth + 1) * 16;
          i.style.background = "rgb(" + c + "," + c + "," + c + ")";
          document.body.appendChild(i);
      }
    </script>
  </head>

  <body>
    <div id="interaction">
      <p style="font-size:40px;">
        Click
        <input type="button"
               style="font-size:40px;"
               value="here"
               onclick="displayKeyboard();">
        to display default style keyboard. (Type a Q or tap somewhere in the page to hide it again.)
      </p>
      <p style="font-size:40px;">
        Click
        <input type="button"
               style="font-size:40px;"
               value="here"
               onclick="displayKeyboard('decimalPad');">
        to display numeric keypad.

      </p>
      <p id="editable-text" style="font-size:40px;">
        The text you type is inserted after this. You can backspace
        into this text, too.
      </p>
      <script>
        // Remove extra whitespace (that doesn't affect formatting but is confusing when you type
        // Backspace) from the editable text.
        var t = document.getElementById("editable-text");
        t.innerHTML = t.innerHTML.replace(/\s+/g, " ").replace(/^ /, "").replace(/ $/, "");
      </script>
      <p style="font-size:40px;">
        Click
        <input type="button"
               style="font-size:40px;"
               value="here"
               onclick="addIframe();">
        to add an iframe with the same contents.
      </p>
    </div>
  </body>
</html>
